package com.gbase8c.dmt.model.migration.dto;

import com.gbase8c.dmt.model.enums.MigrationObjectType;
import com.gbase8c.dmt.model.migration.config.MigrateConfig;
import com.gbase8c.dmt.model.migration.config.Task;
import com.gbase8c.dmt.model.enums.Status;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DboDto {

    private String id;
    private Task task;
    private List<SchemaDto> schemaDtos;
    private List<TableSpaceDto> tableSpaceDtos;

    public List<SchemaDto> getSchemaDtos() {
        return schemaDtos != null ? schemaDtos : Lists.newArrayList();
    }

    public List<TableSpaceDto> getTableSpaceDtos() {
        return tableSpaceDtos != null ? tableSpaceDtos : Lists.newArrayList();
    }

    public Map<MigrationObjectType, List<? extends MigrationObject>> dbObjects() {
        Map<MigrationObjectType, List<? extends MigrationObject>> dbObjects = Maps.newLinkedHashMap();
        Task task = getTask();
        MigrateConfig migrateConfig = task.getMigrateConfig();

        List<SchemaDto> schemaDtos = getSchemaDtos();

        dbObjects.put(MigrationObjectType.SCHEMA, schemaDtos);

        List<SequenceDto> sequenceDtos = getSchemaDtos().stream()
                .flatMap(schemaDto -> schemaDto.getSequenceDtos().stream())
                .collect(Collectors.toList());
        dbObjects.put(MigrationObjectType.SEQUENCE, sequenceDtos);

        // tableSpace
        List<TableSpaceDto> tableSpaceDtos = new ArrayList<>(getTableSpaceDtos());
        dbObjects.put(MigrationObjectType.TABLESPACE, tableSpaceDtos);

        //  table
        List<TableDto> tableDtos = getSchemaDtos().stream()
                .flatMap(schemaDto -> schemaDto.getTableDtos().stream())
                .collect(Collectors.toList());

        if (migrateConfig.getRecreateTable()) {
            dbObjects.put(MigrationObjectType.TABLE, tableDtos);
        }

        // index
        if (migrateConfig.getMigrateIndex()) {
            List<IndexDto> indexDtos = getSchemaDtos().stream()
                    .flatMap(schemaDto -> schemaDto.getIndexDtos().stream())
                    .collect(Collectors.toList());
            dbObjects.put(MigrationObjectType.INDEX, indexDtos);
        }

        Map<String, List<ConstraintDto>> constraintDtos = getSchemaDtos().stream()
                .flatMap(schemaDto -> schemaDto.getConstraintDtos().stream())
                .collect(Collectors.groupingBy(ConstraintDto::getTarType));
        // pk
        if (migrateConfig.getMigratePk()) {
            dbObjects.put(MigrationObjectType.PK, constraintDtos.get("PRIMARY KEY"));
        }
        // unique
        if (migrateConfig.getMigrateUniqueConstraint()) {
            dbObjects.put(MigrationObjectType.UNIQUE, constraintDtos.get("UNIQUE"));
        }
        // fk
        if (migrateConfig.getMigrateFk()) {
            dbObjects.put(MigrationObjectType.FK, constraintDtos.get("FOREIGN KEY"));
        }
        // check
        if (migrateConfig.getMigrateCheckConstraint()) {
            dbObjects.put(MigrationObjectType.CHECK, constraintDtos.get("CHECK"));
        }

        // view
        List<ViewDto> viewDtos = getSchemaDtos().stream()
                .flatMap(schemaDto -> schemaDto.getViewDtos().stream())
                .collect(Collectors.toList());
        dbObjects.put(MigrationObjectType.VIEW, viewDtos);

        // function
        List<FunctionDto> functionDtos = getSchemaDtos().stream()
                .flatMap(schemaDto -> schemaDto.getFunctionDtos().stream())
                .collect(Collectors.toList());
        dbObjects.put(MigrationObjectType.FUNCTION, functionDtos);

        // trigger
        List<TriggerDto> triggerDtos = getSchemaDtos().stream()
                .flatMap(schemaDto -> schemaDto.getTriggerDtos().stream())
                .collect(Collectors.toList());
        dbObjects.put(MigrationObjectType.TRIGGER, triggerDtos);

        // synonym
        List<SynonymDto> synonymDtos = getSchemaDtos().stream()
                .flatMap(schemaDto -> schemaDto.getSynonymDtos().stream())
                .collect(Collectors.toList());
        dbObjects.put(MigrationObjectType.SYNONYM, synonymDtos);

        return dbObjects;
    }

    public PrecheckResult toPrecheckResult(List<MigrationObjectType> orders) {
        PrecheckResult precheckResult = PrecheckResult.builder()
                .id(id)
                .build();
        Map<MigrationObjectType, List<? extends MigrationObject>> dbObjects = dbObjects();
        for (MigrationObjectType migrationObjectType : orders) {
            if (dbObjects.get(migrationObjectType) != null && !dbObjects.get(migrationObjectType).isEmpty()) {
                List<? extends MigrationObject> mos = dbObjects.get(migrationObjectType);
                for (MigrationObject mo : mos) {
                    PrecheckResult.PrecheckRecord record = PrecheckResult.PrecheckRecord.builder()
                            .key(mo.key())
                            .type(mo.getMigrationObjectType())
                            .schema(mo.getSchema())
                            .tableName(mo.getTableName())
                            .name(mo.getName())
                            .contents(mo.getContents())
                            .customContents(mo.getContents())
                            .modified(false)
                            .status(Status.UnStart)
                            .build();
                    precheckResult.addRecord(record);
                }

            }
        }
        return precheckResult;
    }
}
